<template>
  <Teleport to="body">
    <transition name="menus-fade">
      <div
        v-if="openFlag"
        ref="menusRef"
        class="menus"
        :style="{
          top: `${Style.top}px`,
          left: `${Style.left}px`,
          zIndex: Style.zIndex,
        }"
        @contextmenu="(e) => e.preventDefault()"
        @mousewheel.stop
      >
        <div
          class="menuItem"
          v-for="(item, index) in menus"
          :key="item.label"
          @click="handleItemClick(item)"
          @mouseenter="
            (event) => {
              menuEnter(event, item, index);
            }
          "
        >
          {{ item.label }}
          <div class="menuItemIcon" v-if="item.children?.length">
            <el-icon :size="14" color="#333"><CaretRight /></el-icon>
          </div>
        </div>
      </div>
    </transition>
  </Teleport>
</template>

<script lang="ts">
import {
  defineComponent,
  ref,
  onMounted,
  onUnmounted,
  nextTick,
  h,
  createApp,
} from "vue";
import { MenuItemType } from "../event";
import Menus from "./Menus.vue";
import { CaretRight } from "@element-plus/icons-vue";

export default defineComponent({
  name: "ContextMenu",
  props: {
    position: {
      type: Object,
      default: {},
    },
    event: {
      type: Object,
      default: {},
    },
    menus: {
      type: Array<MenuItemType>,
      default: [],
    },
    direction: {
      type: String,
      defalut: "right",
    },
    zIndex: {
      type: [Number, String],
      default: 5,
    },
  },
  components: {
    CaretRight,
  },
  setup(props) {
    const ctx: any = {};
    const windowWidth = document.documentElement.clientWidth;
    const windowHeight = document.documentElement.clientHeight;

    const openFlag = ref(false);
    const Style = ref({
      left: 0,
      top: 0,
      zIndex: props.zIndex,
    });
    const menusRef = ref<null | HTMLElement>(null);
    const activeIndex = ref(-1);

    const Position =
      props.position.x && props.position.y
        ? ref({ ...props.position })
        : ref({
            x: props.event.clientX,
            y: props.event.clientY,
            width: 0,
            height: 0,
          });

    const direction = ref(props.direction);

    function leftOpen(menuWidth: number) {
      Style.value.left = Position.value.x - menuWidth;
      direction.value = "left";
      if (Style.value.left < 0) {
        direction.value = "right";
        if (!Position.value.width) {
          Style.value.left = 0;
        } else {
          Style.value.left = Position.value.x + Position.value.width;
        }
      }
    }

    function rightOpen(menuWidth: number) {
      Style.value.left = Position.value.x + Position.value.width;
      direction.value = "right";
      if (Style.value.left + menuWidth > windowWidth) {
        direction.value = "left";
        if (!Position.value.width) {
          Style.value.left = windowWidth - menuWidth;
        } else {
          Style.value.left = Position.value.x - menuWidth;
        }
      }
    }

    function close() {
      openFlag.value = false;
      if (ctx && ctx.instance) {
        ctx.instance.close();
      }

      nextTick(() => {
        this.$unmount() && this.$unmount();
      });
    }

    function handleItemClick(menu: MenuItemType) {
      if (typeof menu.click === "function") {
        menu.click();
      }
    }

    function menuEnter(event: any, item, index: number) {
      activeIndex.value = index;
      if (ctx.instance) {
        if (ctx.index === index) {
          return;
        }
        // 如果有子menu弹出来了，再移动进来就关闭子菜单

        ctx.instance.close();
        ctx.instance = null;
        ctx.index === null;
      }
      if (!item.children) {
        return;
      }
      const menuItemClientRect = event.target.getBoundingClientRect();
      const node = h(Menus, {
        ...props,
        menus: item.children || [],
        position: {
          x: menuItemClientRect.x - 3,
          y: menuItemClientRect.y - 6,
          width: menuItemClientRect.width - 6,
          height: menuItemClientRect.width,
        },
      });
      const childApp = createApp(node);
      ctx.instance = childApp.mount(document.createElement("div"));
      ctx.instance.$unmount = childApp.unmount;
      ctx.index = index;
      event.preventDefault();
    }

    onMounted(() => {
      openFlag.value = true;

      nextTick(() => {
        if (menusRef.value) {
          const menuWidth = menusRef.value.offsetWidth;
          const menuHeight = menusRef.value.offsetHeight;
          if (direction.value === "left") {
            leftOpen(menuWidth);
          } else {
            rightOpen(menuWidth);
          }
          Style.value.top = Position.value.y;
          if (Style.value.top + menuHeight > windowHeight) {
            if (!Position.value.height) {
              Style.value.top = Position.value.y - menuHeight;
            } else {
              Style.value.top = windowHeight - menuHeight;
            }
          }
        }
      });
    });

    return { openFlag, Style, close, menusRef, handleItemClick, menuEnter };
  },
});
</script>

<style lang="less">
.menus {
  position: fixed;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
  background: #fff;
  border-radius: 6px;
  z-index: 199;
  padding: 8px 0px;
  user-select: none;
  box-sizing: border-box;
  .menuItem {
    height: 36px;
    display: flex;
    align-items: center;
    align-content: center;
    justify-content: space-between;
    color: #333;
    font-size: 14px;
    min-width: 200px;
    cursor: pointer;
    padding: 0 8px;
    &:hover {
      background-color: #f2f2f2;
    }
    .menuItemIcon {
      display: flex;
      height: 100%;
      align-items: center;
    }
  }
}

.menus_body {
  display: block;
}

.menus-fade-enter-active,
.menus-fade-leave-active {
  transition: opacity 0.1s ease-in-out;
}
.menus-fade-enter-from,
.menus-fade-leave-to {
  opacity: 0;
}
</style>
